package com.ruoyi.app.utils;

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.app.domain.vo.LoginTokenVO;
import com.ruoyi.app.domain.vo.UserLoginVO;
import com.ruoyi.common.utils.StringUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
public class ApiTokenServiceUtil {

    private static final String ISSUER = "Just API";

    public static final String SECRET_KEY = "JUST_API_1.0";

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    // 令牌有效期（默认30分钟）
    @Value("${api.token.expireTime}")
    private int expireTime;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public LoginTokenVO createToken(String id) {
        final Date tokenExpiredDate = new Date(System.currentTimeMillis() + expireTime * MILLIS_MINUTE);

        String token = Jwts
                .builder()
                .setSubject(id)
                .setIssuedAt(new Date())
                .setIssuer(ISSUER)
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
        return new LoginTokenVO(token, tokenExpiredDate);
    }

    /**
     * 获取用户身份信息
     */
    public UserLoginVO getLoginUser(String token) {
        // 获取请求携带的令牌
        if (StringUtils.isNotEmpty(token)) {
            // 根据Token，解析出具体数据
            final Claims claims = Jwts.parser()
                    .setSigningKey(ApiTokenServiceUtil.SECRET_KEY)
                    .parseClaimsJws(token)
                    .getBody();
            String subject = claims.getSubject();

            // 解析对应的权限以及用户信息
            String userKey = getTokenKey(subject);
            if (Boolean.TRUE.equals(redisTemplate.hasKey(userKey))) {
                return redisCache.getCacheObject(userKey);
            }
            return null;
        }
        return null;
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(UserLoginVO user) {
        if (StringUtils.isNotNull(user) && StringUtils.isNotEmpty(user.getLoginTokenVO().getToken())) {
            refreshToken(user);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token) {
        if (StringUtils.isNotBlank(token)) {
            // 根据Token，解析出具体数据
            final Claims claims = Jwts.parser()
                    .setSigningKey(ApiTokenServiceUtil.SECRET_KEY)
                    .parseClaimsJws(token)
                    .getBody();
            String subject = claims.getSubject();
            String userKey = getTokenKey(subject);

            redisCache.deleteObject(userKey);
        }
    }

    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     */
    public void verifyToken(UserLoginVO user) {
        long anchorExpireTime = user.getLoginTokenVO().getTokenExpiredDate().getTime();
        long currentTime = System.currentTimeMillis();
        //有效期小于1/3自动刷新token有效时间
        if (anchorExpireTime - currentTime <= (expireTime / 3 * MILLIS_MINUTE)) {
            refreshToken(user);
        }
    }

    /**
     * 刷新令牌有效期
     */
    public void refreshToken(UserLoginVO anchor) {
        anchor.getLoginTokenVO().setTokenExpiredDate(new Date(System.currentTimeMillis() + expireTime * MILLIS_MINUTE));
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(anchor.getId().toString());
        redisCache.setCacheObject(userKey, anchor, expireTime, TimeUnit.MINUTES);
    }

    private String getTokenKey(String uuid) {
        return Constants.API_LOGIN_TOKEN_KEY + uuid;
    }

    /**
     * 根据 userId 删除用户 token 信息
     */
    public void delLoginUserById(Long userId) {
        String userKey = getTokenKey(userId.toString());
        redisCache.deleteObject(userKey);
    }
}
